/*================================================================
*   Copyright (C) 2024 baichao All rights reserved.
*
*   文件名称：thread_specific_data.c
*   创 建 者：baichao
*   创建日期：2024年03月27日
*   描    述：
*     这四个函数是 POSIX 线程库中用于线程特定数据（Thread-Specific Data，简称TSD）管理的函数。线程特定数据允许线程独立地拥有自己的数据副本，每个线程可以使用相同的键来访问不同的数据副本。
*      1. `int pthread_key_create(pthread_key_t *key, void (*destructor)(void*))`
*         - 函数功能：创建线程特定数据的键。
*         - 参数：
*           - `key`：指向 `pthread_key_t` 类型的指针，用于存储创建的键。
*           - `destructor`：指向清理函数的函数指针，用于在线程退出时销毁线程特定数据。可以将其设置为 `NULL`，表示不需要清理函数。
*         - 返回值：若成功，返回 0；否则返回错误码。
*         - 说明：`pthread_key_create` 函数用于创建一个线程特定数据的键。线程特定数据键用于标识线程特定数据的副本，可以在多个线程间共享。每个线程可以使用相同的键来访问不同的数据副本。`destructor` 参数指定了在线程退出时如何销毁线程特定数据的函数。如果指定了清理函数，它将在线程退出时被调用。
*      
*      2. `int pthread_key_delete(pthread_key_t key)`
*         - 函数功能：删除线程特定数据的键。
*         - 参数：
*           - `key`：要删除的线程特定数据的键。
*         - 返回值：若成功，返回 0；否则返回错误码。
*         - 说明：`pthread_key_delete` 函数用于删除之前创建的线程特定数据的键。它会清理与该键关联的所有线程特定数据，并释放相关资源。请注意，删除键不会自动删除与键关联的数据。如果需要在删除键之前释放数据，请使用清理函数。
*      
*      3. `int pthread_setspecific(pthread_key_t key, const void *value)`
*         - 函数功能：将线程特定数据的值设置为指定的值。
*         - 参数：
*           - `key`：线程特定数据的键。
*           - `value`：要设置的线程特定数据的值。
*         - 返回值：若成功，返回 0；否则返回错误码。
*         - 说明：`pthread_setspecific` 函数将线程特定数据的值设置为指定的值。每个线程都有自己的数据副本，通过键来访问。不同线程可以使用相同的键，但访问的数据副本是独立的。
*      
*      4. `void *pthread_getspecific(pthread_key_t key)`
*         - 函数功能：获取线程特定数据的值。
*         - 参数：
*           - `key`：线程特定数据的键。
*         - 返回值：返回线程特定数据的值。
*         - 说明：`pthread_getspecific` 函数用于获取线程特定数据的值。通过指定键，可以获取当前线程的特定数据值。每个线程都有独立的数据副本，通过键来区分。
*      
*      这些函数可以帮助线程在多线程环境中管理和访问独立的线程特定数据。通过创建键、设置值、获取值和删除键等操作，可以实现线程间的数据隔离和独立性。
*
*   进程中的pthread_key_t最多允许有1024个，即pthread_key_t的变量值（也称键值）总是要小于1024？那是因为NPTL实现一共提供了1024个槽位。
*
================================================================*/

#include <malloc.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
/* 用于为每个线程保存文件指针的 TSD 键值,根据键值可以找到线程各自的Data。*/
static pthread_key_t thread_log_key;

void write_to_thread_log(const char *message) {
  FILE *thread_log = (FILE *)pthread_getspecific(thread_log_key);
  fprintf(thread_log, "%s\n", message);
}

void fn_close_thread_log(void *thread_log) { fclose((FILE *)thread_log); }

void *thread_function(void *args) {
  char thread_log_filename[128];
  FILE *thread_log_fd;
  sprintf(thread_log_filename, "thread%ld.log", (unsigned long)pthread_self());
  thread_log_fd = fopen(thread_log_filename, "w");
  /* 将文件指针保存在thread_log_key标识的 TSD 中。*/
  pthread_setspecific(thread_log_key, thread_log_fd);
  write_to_thread_log("Thread starting.");
  return NULL;
}

int main() {
  int i;
  pthread_t threads[5];
  /* 创建一个键值，用于将线程日志文件指针保存在TSD中。
   * 调用close_thread_log以关闭这些文件指针。*/
  pthread_key_create(&thread_log_key, fn_close_thread_log);
  for (i = 0; i < 5; ++i)
    pthread_create(&(threads[i]), NULL, thread_function, NULL);
  for (i = 0; i < 5; ++i) pthread_join(threads[i], NULL);
  pthread_key_delete(thread_log_key);
  return 0;
}